from random import *
from math import *

class Mutation:

	def flip_one_bit(self, population, probability, sigma=0):
		"""
		Mutation of an individual, calculation a probability for one gene
		in each binary variable
		.Parameters:
			cromo:			Individual, a list of binary lists
			*parameters:	List of parameters. Should contain: probabilities
		.Return value:
			cromo:         	Mutated Individual
		"""
		for p in range(0, len(population)):
			
			cromo=population[p].get_cromo() ## devolve a representacao do inidivuduo - lista de listas para cada var + lista com a prob de mutacao
			n_var=len(cromo)
			
			prob_list = self.prob_list_decoder(population.evol,n_var)
			
			for i in range(0, n_var):
				geneChosen=randint(0, len(cromo[i])-1)
				if(random()<prob_list[i]):
					if cromo[i][geneChosen]==0:
						cromo[i][geneChosen]=1
					else:
						cromo[i][geneChosen]=0
			
		return population

	def flip_one_bit_mutation(self, probability, alpha, sigma):
		return (lambda pop,time: self.flip_one_bit(pop,probability,sigma))


	def flip_several_bits(self, population, probability, sigma=0):
		"""
		Mutation of an individual, calculation a probability for each gene
		.Parameters:
			cromo:			Individual, an object representing it
			*parameters:	List of parameters. Should contain: probabilities
		.Return value:
			cromo:         	Mutated Individual
		"""
		for p in range(0, len(population)):
			
			cromo=population[p].get_cromo() 
			## devolve a representacao do inidivuduo - 
			##lista de listas para cada var + lista com a prob de mutacao
			n_var=len(cromo)
			
			prob_list = self.prob_list_decoder(population.evol,n_var)
			
			for i in range(0, n_var):
				for j in range(0, len(cromo[i])):
					if(random()<prob_list[i]):
						if cromo[i][j]==0:
							cromo[i][j]=1
						else:
							cromo[i][j]=0
				
			#Mutate probabilites (if present in cromossome)
			population.evol = self.mutate_probabilites(population.evol, sigma)
		
		return population

	def flip_several_bits_mutation(self, probability, alpha, sigma):
		return (lambda pop,time=-1: self.flip_several_bits(pop,probability,sigma))


	def flip_bits_position_related(self, population,generations,alfa,sigma=0):
		"""
		***EXPERIMENTAL MUTATION***
		Mutation of an individual, calculation a probability for each gene
		depending on the position in the cromossome, as well as the number of 
		generations and an alpha constant. The probabilty of mutating a gene
		increases as the genes become less significant. The probabilty also
		decreases as the number of generations increases.
		
		.Parameters:
			cromo:			Individual, a list of binary lists
			*parameters:	List of parameters. Should contain: 
							generations, alpha (uma lista - tem um sigma != pra cada variavel)
		
		.Return value:
			cromo:         Mutated Individual
		"""
		
		for p in range(0, len(population)):
			
			cromo=population[p].get_cromo() ## devolve a representacao do inidivuduo - lista de listas para cada var + lista com a prob de mutacao
			n_var=len(cromo)

			for i in range(0,n_var):
				prob=alfa*((1/log(generations+2))/len(cromo[0]))
				
				gene_prob=prob
				for j in range(0, len(cromo[i])):
					if random()<gene_prob:
						if cromo[i][j]==0:
							cromo[i][j]=1
						else:
							cromo[i][j]=0
					gene_prob+=prob

			#Mutate probabilites (if present in cromossome)
			population.evol=self.mutate_probabilites(population.evol, sigma)
			
		return population

	def flip_bits_position_related_mutation(self, prob, alfa, sigma):
		return (lambda pop,time: self.flip_bits_position_related(pop,time,alfa,sigma))


	def mutate_probabilites(self, cromo_probs, sigma): ###o cromo probs pode ser []
		"""
		***POSSIBLE EXPERIMENTAL MUTATION***
		Mutation of the probabilites of the operators of an individual,
		following a Normal distribution
		
		.Parameters:
			cromoProbs:	Probabilites of the operators of an individual
			*parameters:	List of parameters. Should contain: sigma
		
		.Return value:
			cromo:	Mutated Individual
		"""
		
		if cromo_probs.__class__ == list: # e float

			for i in range(0, len(cromo_probs)):
				new_value=cromo_probs[i]+gauss(0, sigma)
				if 1>new_value>0:
					cromo_probs[i]=new_value
					
		return cromo_probs

	def prob_list_decoder(self,evol,size):
		aux_list = []
		if evol.__class__ == float: # e float
			for i in range(size):
				aux_list.append(evol)
		else:
			aux_list=evol
		
		return aux_list

